#include <iostream>
#include <string>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <cstdio>
#include <sys/types.h>
#include <unistd.h>

#define ERR_EXIT(m)         \
    do                      \
    {                       \
        perror(m);          \
        exit(EXIT_FAILURE); \
    } while (0)

const int gdefaultid = -1;
const size_t gsize = 4096;
const std::string pathname = ".";
const int projid = 066;
const int gmode = 0666;
#define USER "user"
#define CREAT "creat"

class Shm
{
private:
    void CreatHelper(int flag)
    {
        _shmid = shmget(_key, _size, flag);
        if (_shmid < 0)
            ERR_EXIT("shmget error");
        printf("创建shm成功\n");
    }

    void Creat()
    {
        CreatHelper(IPC_CREAT | IPC_EXCL | gmode);
    }

    void Get()
    {
        CreatHelper(IPC_CREAT);
    }

    void Attach()
    {
        _startaddr = shmat(_shmid, NULL, 0);
        if ((long long)_startaddr < 0)
            ERR_EXIT("shmat error");
        printf("shmat success\n");
    }

    void Destory()
    {
        if (_shmid == gdefaultid)
            return;
        //去除绑定
        int n = shmdt(_startaddr);
        if(n<0)
        {
            ERR_EXIT("shmdt error");
        }
        else{
            printf("shmdt success\n");
        }
        //由创建者关闭
        if (_username == CREAT)
        {
            n = shmctl(_shmid, IPC_RMID, nullptr);
            if (n == 0)
            {
                printf("shmctl delete shmid:%d success\n", _shmid);
            }
            else
            {
                ERR_EXIT("shmctl error");
            }
        }
    }

public:
    Shm(const std::string &pathname, int projid, const std::string &username)
        : _shmid(gdefaultid), _size(gsize), _startaddr(nullptr), _username(username)
    {
        // 1.初始化_key
        _key = ftok(pathname.c_str(), projid);
        if (_key < 0)
            ERR_EXIT("ftok error");
        // 2.根据用户名创建或获取shm
        if (username == CREAT)
            Creat();
        else if (username == USER)
            Get();
        else
        {
        }
        // 3.挂接
        Attach();
    }

    void *Virtualaddr() { return _startaddr; }

    size_t Size() { return _size; }

    ~Shm()
    {
        Destory();
    }

private:
    key_t _key;
    int _shmid;
    size_t _size;
    void *_startaddr;
    std::string _username;
};